package lib;

import gui.dialog.SelectDialog;

import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import javax.swing.JFrame;

import lib.control.VideoFileParser;
import lib.control.VideoPatterns;
import lib.library.FilenameLibraryBuilder;
import lib.library.LibraryActionType;
import lib.library.LibraryOtherFileActionType;
import lib.library.PathLibraryBuilder;
import lib.logging.Logger;
import lib.model.Subtitle;
import lib.model.SubtitleMatchType;
import lib.model.VideoFile;
import lib.model.VideoType;
import settings.model.LibrarySettings;
import settings.model.Settings;
import settings.model.SettingsExcludeType;
import util.Downloader;
import util.FilenameContainsFilter;
import util.FilenameExtensionFilter;
import util.Files;
import util.NamedMatcher;
import util.NamedPattern;
import util.StringUtils;

public class Actions {

    private final Settings settings;
    private final boolean usingCMD;

    public Actions(Settings settings, final boolean usingCMD) {
        this.settings = settings;
        this.usingCMD = usingCMD;
    }

    public int determineWhatSubtitleDownload(final VideoFile videoFile, final boolean subtitleSelectionDialog) {
        if (videoFile.getMatchingSubs().size() > 0) {
            Logger.instance.debug("determineWhatSubtitleDownload: # found subs: " + videoFile.getMatchingSubs().size());
            if (settings.isOptionsAlwaysConfirm() && !usingCMD) {
                int selected = getSelectedInDialog(null, videoFile);
                if (selected >= 0)
                    return selected;
            } else if (videoFile.getMatchingSubs().size() == 1
                    && videoFile.getMatchingSubs().get(0).getSubtitleMatchType() == SubtitleMatchType.EXACT) {
                Logger.instance.debug("determineWhatSubtitleDownload: Exact Match");
                return 0;
            } else if (settings.isOptionsAutomaticDownloadSelection()) {
                Logger.instance.debug("determineWhatSubtitleDownload: Automatic Download Selection");
                int selected = getAutomaticSubtitleSelection(videoFile.getMatchingSubs());
                if (selected >= 0)
                    return selected;
            } else if (videoFile.getMatchingSubs().size() > 1) {
                //show message for logging
                Logger.instance.debug("determineWhatSubtitleDownload: Multiple subs detected");
                if (subtitleSelectionDialog) {
                	Logger.instance.debug("determineWhatSubtitleDownload: Select subtitle with dialog");
                	if (usingCMD){
                		int selected = getSelectedInCMD(videoFile);
                        if (selected >= 0)
                        	return selected;
                	}else{
                		 int selected = getSelectedInDialog(null, videoFile);
                         if (selected >= 0)
                             return selected;
                	}
                }else{
                	Logger.instance.log("Multiple subs detected for: " + videoFile.getFilename()
                            + " Unhandleable for CMD! switch to GUI"
                			+ " or use '--selection' as switch in de CMD");

                }
            } else if (videoFile.getMatchingSubs().size() == 1) {
                Logger.instance.debug("determineWhatSubtitleDownload: only one sub taking it!!!!");
                return 0;
            }
        }
        Logger.instance.debug("determineWhatSubtitleDownload: No subs found for: " + videoFile.getFilename());
        return -1;
    }

    private int getSelectedInCMD(VideoFile videoFile) {
    	for (int i = 0; i < videoFile.getMatchingSubs().size(); i++) {
            System.out.println("(" + i + ")" 
            		+ videoFile.getMatchingSubs().get(i).getFilename() 
            		+ " (Source: " 
            		+ videoFile.getMatchingSubs().get(i).getSubtitleSource() 
            		+ ") ");
        }
    	Console c = System.console();
    	String selectedSubtitle = c.readLine("Enter number of selected subtitle: ");
    	try{
    		Integer.parseInt(selectedSubtitle);
    	}catch (Exception e){
    		return -1;
    	}
		return Integer.parseInt(selectedSubtitle);
	}

	protected static int getSelectedInDialog(JFrame frame, VideoFile videoFile) {
        final SelectDialog sDialog = new SelectDialog(frame, videoFile.getMatchingSubs(), videoFile.getFilename());

        if (sDialog.getAnswer() == SelectDialog.SelectionType.OK) {
            return sDialog.getSelection();
        }
        return sDialog.getAnswer().getSelectionCode();
    }

    public List<File> getFileListing(File dir, boolean recursieve, String languagecode, boolean forceSubtitleOverwrite) {
        Logger.instance.trace("Actions", "getFileListing", 
                "dir: " + dir +
                " recursieve: " + recursieve +
                " languagecode: " + languagecode +
                " forceSubtitleOverwrite: " + forceSubtitleOverwrite);
        final List<File> filelist = new ArrayList<File>();
        final File[] contents = dir.listFiles();
        if (contents != null){
            for (final File file : contents) {
                if (file.isFile()) {
                    if (isValidVideoFile(file) && (!fileHasSubtitles(file, languagecode) || forceSubtitleOverwrite)
                            && isNotExcluded(file)) {
                        filelist.add(file);
                    }
                } else if (recursieve) {
                    if (settings.getExcludeList().size() == 0) {
                        filelist.addAll(getFileListing(file, recursieve, languagecode, forceSubtitleOverwrite));
                    } else {
                        Boolean status = true;
                        for (int j = 0; j < settings.getExcludeList().size(); j++) {
                            if (settings.getExcludeList().get(j).getType() == SettingsExcludeType.FOLDER) {
                                File excludeFile = new File(settings.getExcludeList().get(j).getDescription());
                                if (excludeFile.equals(file)) {
                                    Logger.instance.trace("Actions", "getFileListing", 
                                            "Skipping: " + file);
                                    status = false;
                                    j = settings.getExcludeList().size();
                                }
                            }
                        }
                        if (status) {
                            filelist.addAll(getFileListing(file,
                                    recursieve, languagecode, forceSubtitleOverwrite));
                        }
                    }
                }
            }
        }
        return filelist;
    }

    private boolean isNotExcluded(File file) {
        for (int j = 0; j < settings.getExcludeList().size(); j++) {
            if (settings.getExcludeList().get(j).getType() == SettingsExcludeType.REGEX) {
                NamedPattern np = NamedPattern.compile(
                        settings.getExcludeList().get(j).getDescription().replace("*", ".*") + ".*$", 
                        Pattern.CASE_INSENSITIVE);
                NamedMatcher namedMatcher = np.matcher(file.getName());
                if (namedMatcher.find()) {
                    Logger.instance.trace("Actions", "isNotExcluded", 
                            "Skipping: " + file);
                    return false;
                }
            }
        }
        for (int j = 0; j < settings.getExcludeList().size(); j++) {
            if (settings.getExcludeList().get(j).getType() == SettingsExcludeType.FILE) {
                File excludeFile = new File(settings.getExcludeList().get(j).getDescription());
                if (excludeFile.equals(file)) {
                    Logger.instance.trace("Actions", "isNotExcluded", 
                            "Skipping: " + file);
                    return false;
                }
            }
        }
        return true;
    }

    public boolean isValidVideoFile(File file) {
        final String filename = file.getName();
        final int mid = filename.lastIndexOf(".");
        final String ext = filename.substring(mid + 1, filename.length());
        if (filename.contains("sample"))
            return false;
        for (String allowedExtension : VideoPatterns.EXTENSIONS) {
            if (ext.equalsIgnoreCase(allowedExtension))
                return true;
        }
        return false;
    }

    /**
     * @param videoFile
     * @param subtitle
     * @param librarySettings
     * @param version
     * @throws Exception
     */
    public static void download(VideoFile videoFile, Subtitle subtitle, LibrarySettings librarySettings, int version)
            throws Exception {
        Logger.instance.trace("Actions","download","LibraryAction" + librarySettings.getLibraryAction());
        PathLibraryBuilder pathLibraryBuilder = new PathLibraryBuilder(librarySettings);
        final File path = pathLibraryBuilder.buildPath(videoFile);
        if (!path.exists()) {
            Logger.instance.debug("Download creating folder: " + path.getAbsolutePath());
            if (!path.mkdirs()) {
                throw new Exception("Download unable to create folder: "
                        + path.getAbsolutePath());
            }
        }

        FilenameLibraryBuilder filenameLibraryBuilder = new FilenameLibraryBuilder(librarySettings);
        final String videoFileName = filenameLibraryBuilder.buildFileName(videoFile);
        final String subFileName = filenameLibraryBuilder.buildSubFileName(videoFile, subtitle, videoFileName, version);

        boolean success;
        success = Downloader.doDownloadFile(new URL(subtitle.getDownloadlink()), new File(path, subFileName));
        Logger.instance.debug("doDownload file was: " + success);

        if (success) {
            if (!librarySettings.getLibraryAction().equals(LibraryActionType.NOTHING)) {
                final File oldLocationFile = new File(videoFile.getPath(), videoFile.getFilename());
                if (oldLocationFile.exists()) {
                    final File newLocationFile = new File(path, videoFileName);
                    Logger.instance.log("Moving/Renaming " + videoFileName + " to folder " + path.getPath() + " , this might take a while... ");
                    Files.move(oldLocationFile, newLocationFile);
                    if (!librarySettings.getLibraryOtherFileAction().equals(LibraryOtherFileActionType.NOTHING)){
                        cleanUpFiles(librarySettings, videoFile, path, videoFileName);
                    }
                    if (librarySettings.isLibraryRemoveEmptyFolders() && videoFile.getPath().listFiles().length == 0) {
                        videoFile.getPath().delete();
                    }
                }
            }
        }
    }

    public static void rename(LibrarySettings librarySettings, File f,
                              VideoFile videoFile) {
        Logger.instance.trace("Actions","rename","LibraryAction" + librarySettings.getLibraryAction());
        String filename = "";
        if (librarySettings.getLibraryAction().equals(LibraryActionType.RENAME) ||
                librarySettings.getLibraryAction().equals(LibraryActionType.MOVEANDRENAME)){
            FilenameLibraryBuilder filenameLibraryBuilder = new FilenameLibraryBuilder(librarySettings);
            filename = filenameLibraryBuilder.buildFileName(videoFile);
            if (videoFile.getExtension().equals("srt")) {
                String languageCode = "";
                try {
                    if (librarySettings.isLibraryIncludeLanguageCode()) {
                        languageCode =  DetectLanguage.execute(f);
                    }
                } catch (final Exception e) {
                    Logger.instance.error("Unable to detect language, leaving language code blank");
                }
                
                filename = filenameLibraryBuilder.buildSubFileName(videoFile, filename, languageCode, 0);
            }
        }else{
            filename = f.getName();
        }
        Logger.instance.trace("Actions","rename","filename" + filename);
        
        PathLibraryBuilder pathLibraryBuilder = new PathLibraryBuilder(librarySettings);
        final File newDir = pathLibraryBuilder.buildPath(videoFile);
        boolean status = true;
        if (!newDir.exists()) {
            Logger.instance.debug("Creating dir: " + newDir.getAbsolutePath());
            status = newDir.mkdirs();
        }
        
        Logger.instance.trace("Actions","rename","newDir" + newDir);
        
        if (status) {
            final File file = new File(videoFile.getPath(), videoFile.getFilename());

            try {
            	
                if (librarySettings.getLibraryAction().equals(LibraryActionType.MOVE) || 
                        librarySettings.getLibraryAction().equals(LibraryActionType.MOVEANDRENAME)) {
                	Logger.instance.log("Moving " + filename + " to the library folder " + newDir + " , this might take a while... ");
                    Files.move(file, new File(newDir, filename));
                } else {
                	Logger.instance.log("Moving " + filename + " to the library folder " + videoFile.getPath() + " , this might take a while... ");
                    Files.move(file, new File(videoFile.getPath(), filename));
                }
                if (!librarySettings.getLibraryOtherFileAction().equals(LibraryOtherFileActionType.NOTHING)){
                    cleanUpFiles(librarySettings, videoFile, newDir, filename);
                }
                if (librarySettings.isLibraryRemoveEmptyFolders() && videoFile.getPath().listFiles().length == 0) {
                    videoFile.getPath().delete();
                }
            } catch (IOException e) {
                Logger.instance.error("Unsuccessfull in moving the file to the libary");
            }

        }
    }
    

    private static void cleanUpFiles(LibrarySettings librarySettings,
            VideoFile videoFile, File path, String videoFileName) throws IOException { 
        Logger.instance.trace("Actions","cleanUpFiles","LibraryOtherFileAction" + librarySettings.getLibraryOtherFileAction());
        final List<String> filters = new ArrayList<String>();
        filters.add("nfo");
        filters.add("jpg");
        filters.add("sfv");
        filters.add("srr");
        filters.add("srs");
        filters.add("nzb");
        filters.add("torrent");
        final String[] extensions = videoFile.getPath().list(
                new FilenameExtensionFilter(filters.toArray(new String[filters.size()])));
        final String[] contains = videoFile.getPath().list(
                new FilenameContainsFilter("sample"));
        
        //remove duplicates using set
        final Set<String> list = new LinkedHashSet<String>(Arrays.asList(StringUtils.join(extensions, contains)));
        
        if (librarySettings.getLibraryOtherFileAction().equals(LibraryOtherFileActionType.REMOVE)){
            for (String s : list){
                final File file = new File (videoFile.getPath(), s);
                file.delete();
            }
        }else if(librarySettings.getLibraryOtherFileAction().equals(LibraryOtherFileActionType.MOVE)){
            for (String s : list){
                Files.move(new File (videoFile.getPath(), s), new File(path, s));
            }
        }else if(librarySettings.getLibraryOtherFileAction().equals(LibraryOtherFileActionType.MOVEANDRENAME)){
            for (String s : list){
                String extension = VideoFileParser.extractFileNameExtension(s);
                
                if(s.contains("sample")){
                    extension = "sample." + extension;
                }
                    
                final String filename = videoFileName.substring(0, videoFileName.lastIndexOf(".")).concat("." + extension);
                Files.move(new File(videoFile.getPath(), s), new File(path,filename));
            }
        }else if(librarySettings.getLibraryOtherFileAction().equals(LibraryOtherFileActionType.RENAME)){
            for (String s : extensions){
                String extension = VideoFileParser.extractFileNameExtension(s);
                
                if(s.contains("sample")){
                    extension = "sample." + extension;
                }
                
                final String filename = videoFileName.substring(0, videoFileName.lastIndexOf(".")).concat("." + extension);
                Files.move(new File(videoFile.getPath(), s), 
                        new File(videoFile.getPath(),filename));
            }    
        }
    }

    public boolean fileHasSubtitles(File file, String languageCode) {
        String subname = "";
        for (String allowedExtension : VideoPatterns.EXTENSIONS) {
            if (file.getName().contains("." + allowedExtension))
                subname = file.getName().replace("." + allowedExtension, ".srt");
        }

        final File f = new File(file.getParentFile(), subname);
        if (f.exists()) {
            return true;
        } else {
            List<String> filters = new ArrayList<String>();
            if (languageCode.equals("nl")) {
            	filters.add("nld.srt");
            	filters.add("ned.srt");
            	filters.add("dutch.srt");
            	filters.add("dut.srt");
            	filters.add("nl.srt");
            	if (!settings.getEpisodeLibrarySettings().getDefaultNlText().equals(""))
            		filters.add("." + settings.getEpisodeLibrarySettings().getDefaultNlText().concat(".srt"));
                final String[] contents = file.getParentFile().list(
                		new FilenameExtensionFilter(filters.toArray(new String[filters.size()])));
                return checkFileListContent(contents, subname.replace(".srt", ""));
            } else if (languageCode.equals("en")) {
            	filters.add("eng.srt");
            	filters.add("english.srt");
            	filters.add("en.srt");
            	if (!settings.getEpisodeLibrarySettings().getDefaultEnText().equals(""))
            		filters.add("." + settings.getEpisodeLibrarySettings().getDefaultEnText().concat(".srt"));
                final String[] contents = file.getParentFile().list(
                		new FilenameExtensionFilter(filters.toArray(new String[filters.size()])));
                return checkFileListContent(contents, subname.replace(".srt", ""));
            }
        }
        return false;
    }
    
    public boolean checkFileListContent(String[] contents, String subname){
    	if (contents.length > 0){
    		for(final String file: contents){
    			if (file.contains(subname)){
    				return true;
    			}
    		}
    	}
    	return false;
    }

    public int getAutomaticSubtitleSelection(List<Subtitle> matchingSubs) {
        Subtitle subtitle;
        Logger.instance.debug("getAutomaticSubtitleSelection: # quality rules: " + settings.getQualityRuleList().size());
        Logger.instance.debug("getAutomaticSubtitleSelection: quality rules: " + settings.getQualityRuleList().toString());
        for (String quality : settings.getQualityRuleList()) {
            for (int i = 0; i < matchingSubs.size(); i++) {
                subtitle = matchingSubs.get(i);
                Logger.instance.debug("getAutomaticSubtitleSelection: subtitle quality: " + subtitle.getQuality());
                if (quality.equalsIgnoreCase(subtitle.getQuality())) {
                	//subtitle.getQuality().contains(quality)
                    return i;
                }
            }
        }

        if (settings.isOptionsNoRuleMatchTakeFirst()) {
            Logger.instance.debug("getAutomaticSubtitleSelection: Using taking first rule");
            return 0;
        } else {
            Logger.instance.debug("getAutomaticSubtitleSelection: Nothing found");
            return -1;
        }
    }

    public void download(VideoFile videoFile, Subtitle subtitle, int version) throws Exception {
        if (videoFile.getVideoType().equals(VideoType.EPISODE)) {
            download(videoFile, subtitle, settings.getEpisodeLibrarySettings(), version);
        } else if (videoFile.getVideoType().equals(VideoType.MOVIE)) {
            download(videoFile, subtitle, settings.getMovieLibrarySettings(), version);
        }
    }

    public void download(VideoFile videoFile, Subtitle subtitle) throws Exception {
        download(videoFile, subtitle, 0);
    }
}
